package cate.game.res;

import cate.common.table.d.GDObj;
import cate.common.table.d.GDOperation;
import cate.common.util.GameResult;
import cate.common.util.SplitParser;
import cate.game.client.msg.MergeMsgSender;
import cate.game.mail.po.MailBuilder;
import cate.game.role.Role;
import cate.game.role.bag.IBagContainer;
import cate.game.role.bag.item.BaseBagItem;
import com.fasterxml.jackson.annotation.JsonIgnore;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("unchecked")
@NoteClass("混合资源")
public class MixRes {

    private static final Logger logger = LoggerFactory.getLogger(MixRes.class);

    @NoteField("混合资源项")
    public List<MixResItem> items;

    public MixRes() {
        items = new ArrayList<>();
    }

    public MixRes(String config) {
        this.parse(config);
    }

    public MixRes(BaseBagItem instance){
        this();
        items.add(new MixResItem(instance));
    }

    /**
     * 检查是否够消耗 GameResult<缺少的项目>
     */
    public GameResult<MixResItem> consumeCheck(Role role){
        return consumeCheck(role, null);
    }

    /**
     * 解析混合资源配置
     */
    public void parse(String config) {
        try {
            items = SplitParser.parse(MixResItem.class, config);
            if (items == null) {
                items = new ArrayList<>();
            }
        } catch (Exception e) {
            items = new ArrayList<>();
            logger.error("资源解析异常config={}", config, e);
        }
    }

    /**
     * 检查是否够消耗 GameResult<缺少的项目>
     */
    public GameResult<MixResItem> consumeCheck(Role role, CostSelect cs) {
        GameResult<MixResItem> r = combineItems();
        if (!r.ok()) {
            return r;
        }
        if (cs != null) {
            GameResult<Void> checkRes = cs.check();
            if (!checkRes.ok()) {
                return r.fail(checkRes);
            }
        }
        for (MixResItem item : items) {
            if (item.num == 0) {
                continue;
            }
            r = item.consumeCheck(role, cs);
            if (!r.ok()) {
                r.data = item;
                return r;
            }
        }
        return r;
    }

    /**
     * 消耗操作（也会进行消耗检查）
     */
    public GameResult<MixResItem> consume(Role role) {
        return consume(role, null);
    }


    /**
     * 消耗操作（也会进行消耗检查）
     */
    public GameResult<MixResItem> consume(Role role, CostSelect cs) {
        MergeMsgSender sender = new MergeMsgSender();
        GameResult<MixResItem> r = consume(role, cs, sender);
        sender.send(role, true);
        return r;
    }

    /**
     * 消耗操作（也会进行消耗检查）
     */
    public GameResult<MixResItem> consume(Role role, CostSelect cs, MergeMsgSender sender) {
        GameResult<MixResItem> r  = consumeCheck(role, cs);
        if (!r.ok()) {
            return r;
        }
        consumeDirect(role, cs, sender);
        return r;
    }

    @JsonIgnore
    public List<MixResItem> getCombinedItems() {
        combineItems();
        return items;
    }

    private GameResult<MixResItem> combineItems() {
        GameResult<MixResItem> r = new GameResult<>();
        if (isEmpty() || items.size() == 1) {
            return r;
        }
        MixRes newConsume = new MixRes();
        for (MixResItem item : items) {
            r = newConsume.addItemRes(item);
            if (!r.ok()) {
                return r;
            }
        }
        this.items = newConsume.items;
        return r;
    }

    /**
     * 直接消耗
     */
    public void consumeDirect(Role role) {
        consumeDirect(role, null);
    }

    /**
     * 直接消耗
     */
    public void consumeDirect(Role role, CostSelect cs) {
        MergeMsgSender sender = new MergeMsgSender();
        consumeDirect(role, cs, sender);
        sender.send(role, true);
    }

    /**
     * 直接消耗
     */
    public void consumeDirect(Role role, CostSelect cs, MergeMsgSender sender) {
        for (MixResItem item : items) {
            item.consumeDirect(role, cs, sender);
        }
    }

    /**
     * 检查是否可以添加
     */
    public GameResult<Void> addCheck(Role role) {
        MixResClassified classified = new MixResClassified(this);
        GameResult<Void> r;
        if ((r = addCheckType(role, classified, GDObj.Type.HERO)).ok() &&
                (r = addCheckType(role, classified, GDObj.Type.EQUIP)).ok() &&
                (r = addCheckType(role, classified, GDObj.Type.PROP)).ok() &&
                (r = addCheckType(role, classified, GDObj.Type.HERO_PART)).ok() &&
                (r = addCheckType(role, classified, GDObj.Type.RUNE)).ok() &&
                (r = addCheckType(role, classified, GDObj.Type.SPICE)).ok() &&
                (r = addCheckType(role, classified, GDObj.Type.FAIRY)).ok() &&
                (r = addCheckType(role, classified, GDObj.Type.FURNITURE)).ok() &&
                (r = addCheckType(role, classified, GDObj.Type.GEM)).ok() &&
                (r = addCheckType(role, classified, GDObj.Type.AWAKEN_FAIRY)).ok() &&
                (r = addCheckType(role, classified, GDObj.Type.KITCHENWARE)).ok()
        ) {
        }
        return r;
    }

    /**
     * 检查是否可以添加某类物品
     */
    private GameResult<Void> addCheckType(
            Role role, MixResClassified classified, byte objectType) {
        GameResult<Void> r = new GameResult<>();
        if (!classified.hasItem(objectType)) {
           return r;
        }
        IBagContainer bag = role.getBag().getBag(objectType);
        if (bag == null) {
            return r;
        }
        List<MixResItem> list = classified.getItemList(objectType);
        int resGrid = bag.resGridTotal(list);
        int classifiedBagGrid = classified.bagGrid(bag.getUid());
        if (classifiedBagGrid == 0) {
            classifiedBagGrid = bag.gridEmpty();
        }
        if (resGrid > classifiedBagGrid) {
            return r.fail(String.format("%s背包还需要%d个格子", bag.getBagName(), resGrid - classifiedBagGrid));
        }
        classified.recordBagGrid(bag.getUid(), classifiedBagGrid - resGrid);
        return r;
    }

    /**
     * 添加操作 如果背包满了，并不会发邮件哦
     */
    public GameResult<Void> add(Role role) {
        return add(role, null);
    }

    /**
     * 添加操作 如果背包满了，并不会发邮件哦
     */
    public GameResult<Void> add(Role role, MergeMsgSender sender) {
        return add(role, false, 0, sender);
    }


    public GameResult<Void> add(Role role, boolean sendMailWhenFull) {
        return add(role, sendMailWhenFull, 0);
    }

    public GameResult<Void> addAndDialog(Role role, boolean sendMailWhenFull) {
        return addAndDialog(role, sendMailWhenFull, 0);
    }

    public GameResult<Void> addAndDialog(Role role, boolean sendMailWhenFull, int operation) {
        return addAndDialog(role, sendMailWhenFull, operation, 0);
    }

    public GameResult<Void> addAndDialog(Role role, boolean sendMailWhenFull, int operation, int operationSub) {
        return add(role, sendMailWhenFull, operation, operationSub, null, true);
    }
    /**
     * 添加操作
     *
     * @param sendMailWhenFull 当背包空间不够的时候，就发送邮件
     * @param operation        添加奖励的原因操作
     */
    public GameResult<Void> add(Role role, boolean sendMailWhenFull, int operation) {
        return add(role, sendMailWhenFull, operation, 0);
    }

    public GameResult<Void> add(Role role, boolean sendMailWhenFull, int operation, int operationSub) {
        return add(role, sendMailWhenFull, operation, operationSub, null, false);
    }

    /**
     * 添加操作
     *
     * @param sendMailWhenFull 当背包空间不够的时候，就发送邮件
     * @param operation        添加奖励的原因操作
     */
    public GameResult<Void> add(Role role, boolean sendMailWhenFull, int operation, MergeMsgSender sender) {
        return add(role, sendMailWhenFull, operation, 0, sender, false);
    }

    /**
     * 添加操作
     *
     * @param sendMailWhenFull 当背包空间不够的时候，就发送邮件
     * @param operation        添加奖励的原因操作
     */
    public GameResult<Void> add(Role role, boolean sendMailWhenFull, int operation, int operationSub, MergeMsgSender sender, boolean dialog) {
        GameResult<Void> r = addCheck(role);
        if (r.ok()) {
            boolean needSet = operation > 0 && role.log.getOperation() == 0;
            if(needSet){
                role.log.setOperation(operation);
                role.log.setOperationSub(operationSub);
            }
            for (MixResItem item : items) {
                if(item.num == 0 && item.instance == null){
                    continue;
                }
                item.addDirect(role, sender);
            }
            if(needSet){
                role.log.clearOperation();
            }
        } else {
            if (sendMailWhenFull) {
                String content = "由于您的宝物实在太多了，包裹实在是放不下啦！在 %s 里散落一地的宝物洛依依已经帮您带回来了。";
                operation = operation == 0 ? role.log.getOperation() : operation;
                operationSub = operationSub == 0 ? role.log.getOperationSub() : operationSub;
                // 发送邮件
                MailBuilder mb =
                        new MailBuilder()
                                .setTitle("获得物品")
                                .setContent(String.format(content, GDOperation.getDesc(operation, 0)))
                                .setReward(this)
                                .setOperation(operation)
                                .setOperationSub(operationSub);
                role.getMail().add(mb.build());
                role.getGame().notice.message(role, "背包已满，已转发至邮件");
            }
        }
        if (dialog) {
            role.getGame().notice.dialogReward(role, this);
        }
        return r;
    }

    /**
     * 所有item数量乘以scale倍
     */
    public MixRes multiply(long scale) {
        for (MixResItem item : items) {
            item.multiply(scale);
        }
        return this;
    }

    public MixRes multiplyDouble(double scale) {
        for (MixResItem item : items) {
            item.multiplyDouble(scale);
        }
        return this;
    }

    public MixRes addList(MixRes list) {
        if (list == null || list.isEmpty()) {
            return this;
        }
        for (MixResItem item : list.items) {
            addItem(item);
        }
        return this;
    }

    /**
     * 一定要大的减小的
     *
     * @param list list
     * @return this
     */
    public MixRes minusList(MixRes list) {
        if (list == null || list.items == null) {
            return this;
        }
        for (MixResItem item : list.items) {
            minusItem(item);
        }
        return this;
    }

    public MixRes minusItem(MixResItem item) {
        if (this.items == null) {
            this.items = new ArrayList<>();
        }
        if (item.num == 0) {
            return this;
        }
        for (MixResItem old : this.items) {
            if (old.type == item.type && old.tid == item.tid && item.instance == null) {
                old.num = Math.max(0, old.num - item.num);
                break;
            }
        }
        return this;
    }

    public GameResult<MixResItem> addItemRes(MixResItem item) {
        GameResult<MixResItem> r = new GameResult<>();
        boolean existed = false;
        if (item.num < 0) {
            r.data = item;
            return r.fail("物品数量有误");
        }
        for (MixResItem old : this.items) {
            if (old.type == item.type && old.tid == item.tid && item.instance == null) {
                old.num += item.num;
                if (old.num < item.num || old.num < 0) {
                    r.data = old;
                    return r.fail("物品数量有误");
                }
                existed = true;
                break;
            }
        }
        if (!existed) {
            this.items.add(item.copy());
        }
        return r;
    }

    public MixRes addItem(MixResItem item) {
        if (this.items == null) {
            this.items = new ArrayList<>();
        }
        if (item.num == 0 && item.instance == null) {
            return this;
        }
        if (item.instance != null) {
            this.items.add(item);
            return this;
        }
        boolean existed = false;
        for (MixResItem old : this.items) {
            if (old.type == item.type && old.tid == item.tid && item.instance == null) {
                old.num += item.num;
                existed = true;
                break;
            }
        }
        if (!existed) {
            this.items.add(item.copy());
        }
        return this;
    }

    /**
     * 统计数量
     */
    public long count(
            byte type, int tid) {
        if (this.items == null) {
            return 0;
        }
        long total = 0;
        for (MixResItem item : this.items) {
            if (item.type == type && item.tid == tid) {
                total += item.num;
            }
        }
        return total;
    }

    /**
     * 是否无内容
     */
    @JsonIgnore
    public boolean isEmpty() {
        return CollectionUtils.isEmpty(items) || items.stream().noneMatch(e -> e.num > 0);
    }

    public void clear() {
        items.clear();
    }

    public MixRes copy() {
        MixRes cp = new MixRes();
        if (this.items != null) {
            cp.items = new ArrayList<>();
            this.items.forEach(
                    item -> cp.items.add(item.copy()));
        }
        return cp;
    }

    public static MixRes buildSimple(byte type, int tid, long num) {
        MixRes reward = new MixRes();
        reward.addItem(new MixResItem(type, tid, num));
        return reward;
    }

    @Override
    public String toString() {
        return this.toString(false);
    }

    public String toString(boolean detail) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if (this.items != null) {
            for (MixResItem item : items) {
                sb.append(detail ? item.tid() + "x" + item.num : item);
                sb.append(",");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    public String toItemsStr(){
        StringBuilder sb = new StringBuilder();
        if(CollectionUtils.isNotEmpty(items)){
            for(MixResItem item : items){
                if(StringUtils.isNotBlank(sb)){
                    sb.append(GDObj.AND_PATTERN);
                }
                sb.append(GDObj.getMixResStr(item.type, item.tid, item.num));
            }
        }
        return sb.toString();
    }

    public void itemMultiply(byte type, int tid,double scale) {
        for (MixResItem item : items) {
            if (item.type == type && item.tid == tid) {
                 item.multiplyDouble(scale);
                 return;
            }
        }
    }

    @JsonIgnore
    public long getItemsCount() {
        return items == null ? 0 : items.stream().mapToLong(e -> e.num).sum();
    }
}
